#include "SoundPlay.h"
#include <vector>

using namespace ipsound;

SoundPlay::SoundPlay( MutexBuffer* buffer, LPDIRECTSOUNDBUFFER8 playBuf8 )
:buffer( buffer )
,playBuf8( playBuf8 )
,soundBufOffset( 0 )
,soundBufSize( 0 )
,notifySamplesNum( 4 )
{
	playBuf8->GetFormat( &wfx, sizeof(WAVEFORMATEX), 0 );

	DSBCAPS caps = {0};
	caps.dwSize = sizeof(DSBCAPS);
	playBuf8->GetCaps( &caps );
	soundBufSize = caps.dwBufferBytes;
}

void* SoundPlay::Thread()
{
	LPDIRECTSOUNDNOTIFY8 notify = 0;
	HANDLE bufEvent = CreateEvent( 0, false, false, 0);

	std::vector<DSBPOSITIONNOTIFY> notifyPos( notifySamplesNum );

	playBuf8->QueryInterface(IID_IDirectSoundNotify8, (void**)&notify);

	for(int i = 0; i != notifySamplesNum; ++i)
	{
		notifyPos[i].dwOffset = (soundBufSize * (i + 1) / notifySamplesNum) - 1;
		notifyPos[i].hEventNotify = bufEvent;
	}

	notify->SetNotificationPositions( notifySamplesNum, &notifyPos[0] );
	notify->Release();

	size_t intermedBufSize = soundBufSize / notifySamplesNum;
	void* intermedBuf = malloc( intermedBufSize );
	memset( intermedBuf, 128, intermedBufSize );
	size_t read = buffer->Read( intermedBuf, intermedBufSize );
	PutDataInSoundBuffer( intermedBuf, read );
	
	playBuf8->Play( 0, 0, DSBPLAY_LOOPING );
	ThreadStarted();

	while( true )
	{
		WaitForSingleObject( bufEvent, INFINITE );

		size_t read = buffer->Read( intermedBuf, intermedBufSize );
		PutDataInSoundBuffer( intermedBuf, read );
	}

	free( intermedBuf );
	CloseHandle( bufEvent );
}

void SoundPlay::SetNotifySamplesNum( const unsigned short& notifySamplesNum )
{
	this->notifySamplesNum = notifySamplesNum;
}

SoundPlay::~SoundPlay()
{
}

void SoundPlay::PutDataInSoundBuffer( const void* data, const size_t& size )
{
	void*  lockedDataPtr1 = 0;
	unsigned long lockedDataSize1 = 0;
	void*  lockedDataPtr2 = 0;
	unsigned long lockedDataSize2 = 0;

	if ( playBuf8->Lock(soundBufOffset, size, &lockedDataPtr1, &lockedDataSize1, &lockedDataPtr2, &lockedDataSize2, 0) == DSERR_BUFFERLOST )
	{
		playBuf8->Restore();
		playBuf8->Lock(soundBufOffset, size, &lockedDataPtr1, &lockedDataSize1, &lockedDataPtr2, &lockedDataSize2, 0);
	}

	if(!lockedDataPtr2)
	{
		memcpy(lockedDataPtr1, data, size);
	}
	else
	{
		memcpy(lockedDataPtr1, data, size - lockedDataSize2);
		memcpy(lockedDataPtr2, (char*)data + size - lockedDataSize2, lockedDataSize2);
	}

	playBuf8->Unlock(lockedDataPtr1, lockedDataSize1, lockedDataPtr2, lockedDataSize2);

	soundBufOffset += lockedDataSize1;
	soundBufOffset %= soundBufSize;
	soundBufOffset += lockedDataSize2;
	soundBufOffset %= soundBufSize;
}